#include <arch/interrupts.h>
#include <arch/vectors.h>
#include <arch/cpu.h>
#include <hal/ioapic.h>
#include <keyboard.h>
#include <string.h>
#include <arch/io.h>
#include <log.h>

void keyboard_handler(const isr_param* param) {
    /* 这次中断发生前的上一次中断,以下任意三个键是否有按下 */
    bool ctrl_down_last = ctrl_status;
    bool shift_down_last = shift_status;
    bool caps_lock_last = caps_lock_status;

    bool     break_code;
    u16 scancode = inb(KBD_BUF_PORT);

    /* 若扫描码是e0开头的,表示此键的按下将产生多个扫描码,
     * 所以马上结束此次中断处理函数,等待下一个扫描码进来*/
    if (scancode == 0xe0) {
        ext_scancode = true;  // 打开e0标记
        return;
    }

    /* 如果上次是以0xe0开头,将扫描码合并 */
    if (ext_scancode) {
        scancode = ((0xe000) | scancode);
        ext_scancode = false;  // 关闭e0标记
    }

    break_code = ((scancode & 0x0080) != 0);  // 获取break_code

    if (break_code) {  // 若是断码break_code(按键弹起时产生的扫描码)

        /* 由于ctrl_r 和alt_r的make_code和break_code都是两字节,
        所以可用下面的方法取make_code,多字节的扫描码暂不处理 */
        u16 make_code = (scancode &= 0xff7f);  // 得到其make_code(按键按下时产生的扫描码)

        /* 若是任意以下三个键弹起了,将状态置为false */
        if (make_code == ctrl_l_make || make_code == ctrl_r_make) {
            ctrl_status = false;
        } else if (make_code == shift_l_make || make_code == shift_r_make) {
            shift_status = false;
        } else if (make_code == alt_l_make || make_code == alt_r_make) {
            alt_status = false;
        } /* 由于caps_lock不是弹起后关闭,所以需要单独处理 */

        return;  // 直接返回结束此次中断处理程序

    }
    /* 若为通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_code */
    else if ((scancode > 0x00 && scancode < 0x3b) || (scancode == alt_r_make) || (scancode == ctrl_r_make)) {
        bool shift = false;  // 判断是否与shift组合,用来在一维数组中索引对应的字符
        if ((scancode < 0x0e) || (scancode == 0x29) || (scancode == 0x1a) || (scancode == 0x1b) || (scancode == 0x2b) ||
            (scancode == 0x27) || (scancode == 0x28) || (scancode == 0x33) || (scancode == 0x34) || (scancode == 0x35)) {
            /****** 代表两个字母的键 ********
                 0x0e 数字'0'~'9',字符'-',字符'='
                 0x29 字符'`'
                 0x1a 字符'['
                 0x1b 字符']'
                 0x2b 字符'\\'
                 0x27 字符';'
                 0x28 字符'\''
                 0x33 字符','
                 0x34 字符'.'
                 0x35 字符'/'
            *******************************/
            if (shift_down_last) {  // 如果同时按下了shift键
                shift = true;
            }
        } else {                                      // 默认为字母键
            if (shift_down_last && caps_lock_last) {  // 如果shift和capslock同时按下
                shift = false;
            } else if (shift_down_last || caps_lock_last) {  // 如果shift和capslock任意被按下
                shift = true;
            } else {
                shift = false;
            }
        }

        u8 index = (scancode &= 0x00ff);     // 将扫描码的高字节置0,主要是针对高字节是e0的扫描码.
        char    cur_char = keymap[index][shift];  // 在数组中找到对应的字符

        /* 如果cur_char不为0,也就是ascii码为除'\0'外的字符就加入键盘缓冲区中 */
        if (cur_char) {
            /*****************  快捷键ctrl+l和ctrl+u的处理 *********************
             * 下面是把ctrl+l和ctrl+u这两种组合键产生的字符置为:
             * cur_char的asc码-字符a的asc码, 此差值比较小,
             * 属于asc码表中不可见的字符部分.故不会产生可见字符.
             * 我们在shell中将ascii值为l-a和u-a的分别处理为清屏和删除输入的快捷键*/
            if ((ctrl_down_last && cur_char == 'l') || (ctrl_down_last && cur_char == 'u')) {
                cur_char -= 'a';
            }
            /****************************************************************/

            /* 若kbd_buf中未满并且待加入的cur_char不为0,
             * 则将其加入到缓冲区kbd_buf中 */

            // write_char(&kbd_queue, cur_char);
            printk("key pressed %c\n", cur_char);
            return;
        }

        /* 记录本次是否按下了下面几类控制键之一,供下次键入时判断组合键 */
        if (scancode == ctrl_l_make || scancode == ctrl_r_make) {
            ctrl_status = true;
        } else if (scancode == shift_l_make || scancode == shift_r_make) {
            shift_status = true;
        } else if (scancode == alt_l_make || scancode == alt_r_make) {
            alt_status = true;
        } else if (scancode == caps_lock_make) {
            /* 不管之前是否有按下caps_lock键,当再次按下时则状态取反,
             * 即:已经开启时,再按下同样的键是关闭。关闭时按下表示开启。*/
            caps_lock_status = !caps_lock_status;
        }
    } else {
        // puts("unknown key\n");
        printk("unknown key %d\n", scancode);
    }
}

/* 键盘初始化 */
void keyboard_init() {
    cli();
    u8 irq = ioapic_get_irq(1);
    intr_register_isr(KEYBOARD, &keyboard_handler);
    ioapic_redirect(irq, KEYBOARD, 0, 0);
    sti();
}
